package mspbots.cw.client.core.data.companies;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.cw.client.core.AbstractToolsUtil;
import mspbots.cw.client.core.data.BaseApiService;
import mspbots.cw.client.core.app.SyncRequest;
import mspbots.cw.client.core.client.ClientProperties;
import mspbots.cw.common.*;
import mspbots.cw.common.callback.CallbackAction;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

/**
 * com.mspbots.sync.wise.service.AgreementService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/11/14
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class CompaniesServiceImpl extends AbstractToolsUtil implements BaseApiService {

    @Override
    public Flux<JsonNode> find(Long tenantId, QueryParams queryParams) {
        ResultSync resultSync = ResultSync.withDefault("COMPANIES-SEARCH");
        return this.loadProperties(tenantId)
                .flatMapMany(properties -> this.get(properties, queryParams))
                .contextWrite(ctx -> ctx.put("resultSync", resultSync));
    }

    @Override
    public Mono<JsonNode> count(Long tenantId, QueryParams queryParams) {
        ResultSync resultSync = ResultSync.withDefault("COMPANIES-COUNT");
        return super.loadProperties(tenantId)
                .flatMap(clientProperties -> this.count(clientProperties, queryParams))
                .contextWrite(ctx -> ctx.put("resultSync", resultSync));
    }


    private Mono<JsonNode> count(ClientProperties clientProperties, QueryParams queryParams) {
        return this.cwWebClient.get("/company/companies/count", queryParams)
                .contextWrite(ctx -> ctx.put("properties", clientProperties));
    }

    private Flux<JsonNode> get(ClientProperties properties, QueryParams queryParams) {
        return this.cwWebClient.fetch("/company/companies", queryParams)
                .flatMap(this::setCompanyTeams)
                .contextWrite(ctx -> ctx.put("properties", properties));
    }

    private Mono<JsonNode> setCompanyTeams(JsonNode companyNode) {
        return this.getTeamRoles(companyNode.get("id").intValue())
                .collectList()
                .map(jsonNodeList -> {
                    if (jsonNodeList.size() > 0) {
                        ObjectNode objectNode = companyNode.deepCopy();
                        objectNode.putPOJO("teams", jsonNodeList);
                        return objectNode;
                    }
                    return companyNode;
                }).defaultIfEmpty(companyNode.deepCopy());
    }

    private Flux<JsonNode> getTeamRoles(Integer companyId) {
        return this.cwWebClient.fetch("/company/companies/" + companyId + "/teams", QueryParams.withDefault());
    }

    @Override
    public Mono<Void> syncSlow(ClientProperties properties) {
        ResultSync resultSync = ResultSync.withDefault("COMPANIES-SYNC");
        return super.readAnchorTime(RedisKeys.COMPANIES_ANCHOR_REDIS_KEY_PREFIX + properties.getTenantId())
                .map(syncTime -> QueryParams.withDefault()
                        .setConditions("lastUpdated >[" + syncTime.format(DateTimeFormatter.ISO_DATE_TIME) + "]")
                        .setOrderBy("lastUpdated asc"))
                .flatMapMany(params -> this.get(properties, params))
                .contextWrite(ctx -> ctx.put("resultSync", resultSync))
                .doOnNext(companyNode -> this.syncResult(properties.getTenantId(),
                        resultSync.getRequestId(), companyNode))
                .then();
    }


    private void syncResult(Long tenantId, UUID id, JsonNode resultNode) {
        this.sendEntity(RabbitKeys.COMPANIES_SYNC_QUEUE_KEY, ResultMsg.builder()
                .requestId(id).id(resultNode.get("id").longValue())
                .action(CallbackAction.none.name()).entity(resultNode).tenantId(tenantId)
                .syncDate(LocalDateTime.now()).build());

        this.recordAnchorTime(RedisKeys.COMPANIES_ANCHOR_REDIS_KEY_PREFIX + tenantId,
                resultNode.findValue("lastUpdated").textValue());
    }


    @Override
    public Mono<Void> manualSync(ClientProperties properties, SyncRequest syncRequest) {
        ResultSync resultSync = ResultSync.withDefault("MANUAL-COMPANIES-SYNC");
        String conditionsStr = "lastUpdated>=[" + syncRequest.getBeginTime().format(DateTimeFormatter.ISO_DATE_TIME) +
                "] and lastUpdated <=[" + syncRequest.getEndTime().format(DateTimeFormatter.ISO_DATE_TIME) + "]";
        return this.get(properties, QueryParams.withDefault().setConditions(conditionsStr)
                .setOrderBy("lastUpdated asc"))
                .contextWrite(ctx -> ctx.put("resultSync", resultSync))
                .doOnNext(node -> this.syncResult(properties.getTenantId(),
                        resultSync.getRequestId(), node))
                .then();
    }

}
